﻿//checked
using System;
using System.Data;
using System.Linq;
using System.Collections.Generic;
using net.entity.framework.common;
using net.entity.framework.db;

namespace net.entity.framework.query {
    public static class QueryExecutor {
        #region Methods

        /// <summary>
        /// Fetch a scalar
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="queryBuilder"></param>
        /// <param name="strict">true means throw an exception if the result is not a valid value type (i.e. either null or DBNull)</param>
        /// <returns></returns>
        public static T ExecuteScalar<T>(this QueryBuilder queryBuilder, bool strict) {
            var result = queryBuilder.ExecuteScalar(strict);
            if (null == result || result == DBNull.Value) {
                if (strict) {
                    throw new Exception(string.Format("Got a null/DBNull value, and strict was set"));
                }
                return default(T);
            }
            return (T) result;
        }

        /// <summary>
        /// Fetches a scalar
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="queryBuilder"></param>
        /// <param name="defaultValue"></param>
        /// <returns>defaultValue if value is null</returns>
        public static T ExecuteScalar<T>(this QueryBuilder queryBuilder, T defaultValue) {
            var result = queryBuilder.ExecuteScalar(false);
            if (null == result || result == DBNull.Value) {
                return defaultValue;
            }
            return (T) result;
        }

        /// <summary>
        /// Fetches a nullable scalar
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="queryBuilder"></param>
        /// <returns></returns>
        public static T? ExecuteScalar<T>(this QueryBuilder queryBuilder) where T : struct {
            var result = queryBuilder.ExecuteScalar(false);
            if (null == result || result == DBNull.Value) {
                return new T?();
            }
            return (T) result;
        }

        /// <summary>
        /// Fetches a scalar
        /// </summary>
        /// <param name="queryBuilder"></param>
        /// <param name="strict">true means throw an exception if there is not a "result" (i.e. a table with one row and one column)</param>
        /// <returns></returns>
        private static object ExecuteScalar(this QueryBuilder queryBuilder, bool strict) {
            var commandText = queryBuilder.ToCommandText();
            using (var provider = new DataProvider(EntityGlobalSettings.DbClient)) {
                return Repository.ExecuteScalar(provider, commandText);
            }
        }

        public static IEnumerable<T> Execute<T>(this QueryBuilder queryBuilder) where T : EntityRef {
            var commandText = queryBuilder.ToCommandText();
            var context = queryBuilder.GetQueryContext();
            using (var provider = new DataProvider(EntityGlobalSettings.DbClient)) {
                var ds = Repository.ExecuteQuery(provider, commandText);
                return Fill<T>(ds, context).ToList();
            }
        }

        //public static void Execute(this QueryBuilder[] queryBuilder) {
        //    var commandTexts = new List<string>();
        //    queryBuilder.AsEnumerable().All(x => {
        //                                        commandTexts.Add(x.ToCommandText());
        //                                        return true;
        //                                    });
        //    using (var provider = new DataProvider(DbClient.SqlClient)) {
        //        Repository.ExecuteBatchQuery(provider, commandTexts);
        //    }
        //}

        #endregion

        private static IEnumerable<T> Fill<T>(DataSet ds, QueryContext queryContext) where T : EntityRef {
            foreach (DataRow row in ds.Tables[0].Rows) {
                var type = typeof (T);
                var id = row[0].ToInt32();
                var typeId = row[1].ToInt32();
                var entityRef = EntityInvoker.InvokeEntityRef<T>(type, id, typeId, 0, queryContext);
                yield return entityRef;
            }
        }

        public static int Execute(this DeleteBuilder deleteBuilder) {
            var commandText = deleteBuilder.ToCommandText();
            using (var provider = new DataProvider(EntityGlobalSettings.DbClient)) {
                return Repository.ExecuteNonQuery(provider, commandText);
            }
        }
    }
}